﻿using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using Csla;
using Csla.Rules;
using Csla.Rules.CommonRules;
using Csla.Server;
using BizControl.DalLinq;
using Csla.Data;
using Csla.Reflection;
using System.Data.Linq;
using BizControl.Library.BusinessObjects.Models;
using BizControl.Resources;
using BizControl.Library.Security;
using System.Security;

namespace BizControl.Library.BusinessObjects.People
{
  [Serializable()]
  public class PersonModelBO : BusinessBase<PersonModelBO>
  {
    #region Business Methods

    private static readonly PropertyInfo<ModelInfo> ModelProperty = RegisterProperty<ModelInfo>(c => c.Model);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ModelRequiredPropertyError")]
    public ModelInfo Model
    {
      get { return GetProperty(ModelProperty); }
      private set { SetProperty(ModelProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> PersonProperty = RegisterProperty<PersonInfo>(c => c.Person);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"PersonModelPersonRequiredPropertyError")]
    public PersonInfo Person
    {
      get { return GetProperty(PersonProperty); }
      private set { SetProperty(PersonProperty, value); }
    }

    #region Common properties

    private static readonly PropertyInfo<PersonInfo> CreatedByProperty = RegisterProperty<PersonInfo>(c => c.CreatedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedByProperty")]
    public PersonInfo CreatedBy
    {
      get { return GetProperty(CreatedByProperty); }
      private set { SetProperty(CreatedByProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> CreatedOnProperty = RegisterProperty<DateTime>(c => c.CreatedOn);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedOnRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedOnProperty")]
    public DateTime CreatedOn
    {
      get { return GetProperty(CreatedOnProperty); }
      private set { SetProperty(CreatedOnProperty, value); }
    }

    #endregion

    internal void LoadData(PersonModel personModelDB)
    {
      if (personModelDB == null) return;
      if (Person == null)
        Person = new PersonInfo(personModelDB.PersonRef);
      if (Model == null)
        Model = new ModelInfo(personModelDB.ModelRef);

      // Common properties
      CreatedBy = new PersonInfo(personModelDB.CreatedByRef);
      CreatedOn = personModelDB.CreatedOn;
    }

    private void LinkModelWithPerson(PersonBO person, ModelBO model)
    {
      if (person != null && !person.IsEmpty()) Person = new PersonInfo(person);
      if (model != null && !model.IsEmpty()) Model = new ModelInfo(model);
    }

    #endregion

    #region Business Rules

    protected override void AddBusinessRules()
    {
      base.AddBusinessRules();
      BusinessRules.AddRule(new PersonAndModelCantBeEmpty { PrimaryProperty = PersonProperty });
      BusinessRules.AddRule(new PersonAndModelCantBeEmpty { PrimaryProperty = ModelProperty });
    }

    private class PersonAndModelCantBeEmpty : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (PersonModelBO)context.Target;
        var person = target.ReadProperty(PersonProperty);
        if (person != null && person.IsEmpty())
          context.AddErrorResult(ValidationErrors.PersonModelPersonRequiredPropertyError);
        var model = target.ReadProperty(ModelProperty);
        if (model != null && model.IsEmpty())
          context.AddErrorResult(ValidationErrors.ModelRequiredPropertyError);
      }
    }

    #endregion

    #region  Data Access

    private void Child_Create(PersonBO person, ModelBO model)
    {
      var currentUser = SecurityChecks.GetCurrentUser();
      using (BypassPropertyChecks)
      {
        LinkModelWithPerson(person, model);
        CreatedBy = new PersonInfo(currentUser);
        CreatedOn = DateTime.Now;
      }
      base.Child_Create();
    }

    private void Child_Fetch(PersonModel data, PersonBO person, ModelBO model)
    {
      using (BypassPropertyChecks)
      {
        LinkModelWithPerson(person, model);
        LoadData(data);
      }
    }

    private void Child_Insert()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        using (BypassPropertyChecks)
        {
          ctx.DataContext.AddPersonModel(
            Model == null || Model.IsEmpty() ? (Guid?)null : Model.Id,
            Person == null || Person.IsEmpty() ? (Guid?)null : Person.Id,
            CreatedOn,
            CreatedBy.Id);
        }
      }
    }

    private void Child_DeleteSelf()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        ctx.DataContext.DeletePersonModel(Model.Id, Person.Id);
      }
    }

    #endregion

    #region  Factory Methods

    internal static PersonModelBO NewPersonModel(PersonBO person, ModelBO model)
    {
      if (person == null) throw new ArgumentNullException(ExceptionsMessages.PersonNullReference);
      if (model == null) throw new ArgumentNullException(ExceptionsMessages.ModelNullReference);
      return Csla.DataPortal.CreateChild<PersonModelBO>(person, model);
    }

    internal static PersonModelBO GetPersonModel(PersonModel data, PersonBO person)
    {
      if (person == null) throw new ArgumentNullException(ExceptionsMessages.PersonNullReference);
      return Csla.DataPortal.FetchChild<PersonModelBO>(data, person, null);
    }

    internal static PersonModelBO GetPersonModel(PersonModel data, ModelBO model)
    {
      if (model == null) throw new ArgumentNullException(ExceptionsMessages.ModelNullReference);
      return Csla.DataPortal.FetchChild<PersonModelBO>(data, null, model);
    }

    private PersonModelBO()
    { /* require use of factory methods */ }

    #endregion
  }
}
